package com.config;

import com.config.shiro.CustomRealm;
import com.config.shiro.RedisSessionDAO;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Hash;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class ShiroConfig implements ApplicationRunner {

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * Username Password    Roles
     * joe      password	user
     * jill     password	admin
     */
    // @Bean
    // public Realm realm() {
    //     TextConfigurationRealm realm = new TextConfigurationRealm();
    //     realm.setUserDefinitions("joe=password,user\njill=password,admin");
    //     realm.setRoleDefinitions("admin=read,write\nuser=read");
    //     realm.setCachingEnabled(true);
    //     return realm;
    // }

    /**
     * 密码加密
     *
     * @see Hash#getAlgorithmName()
     */
    @Bean
    public Realm realm(DataSource dataSource) {
        // 加密方式
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME);
        // credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
        // 加密次数
        // credentialsMatcher.setHashIterations(1024);
        credentialsMatcher.setStoredCredentialsHexEncoded(false);

        JdbcRealm jdbcRealm = new CustomRealm();
        jdbcRealm.setName("jdbcRealm");
        jdbcRealm.setCredentialsMatcher(credentialsMatcher);
        jdbcRealm.setDataSource(dataSource);

        return jdbcRealm;
    }

    /**
     * @see DefaultFilter
     * anon                 org.apache.shiro.web.filter.authc.AnonymousFilter
     * authc	            org.apache.shiro.web.filter.authc.FormAuthenticationFilter
     * authcBasic           org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
     * logout	            org.apache.shiro.web.filter.authc.LogoutFilter
     * noSessionCreation	org.apache.shiro.web.filter.session.NoSessionCreationFilter
     * perms	            org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
     * port	                org.apache.shiro.web.filter.authz.PortFilter
     * rest	                org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
     * roles	            org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
     * ssl	                org.apache.shiro.web.filter.authz.SslFilter
     * user	                org.apache.shiro.web.filter.authc.UserFilter
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        chainDefinition.addPathDefinition("/login", "anon");
        chainDefinition.addPathDefinition("/index.html", "anon");
        chainDefinition.addPathDefinition("/zxc", "anon");
        // spring 中访问不存的 url 会跳转到 /error
        chainDefinition.addPathDefinition("/error", "anon");

        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // https://blog.csdn.net/f641385712/article/details/88651128
        // 真实的实现类一般都是：DefaultListableBeanFactory
        AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
        // 创建过程交给 Bean 工厂（createBean方法执行多次，就会创建多个实例）
        // Bean 并没交给 Spring 容器管理，它只是帮我们创建好了，并把对应属性注入进去了
        RedisSessionDAO sessionDAO = (RedisSessionDAO) autowireCapableBeanFactory.createBean(RedisSessionDAO.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);

        // 将 shiro 中登陆成功后的 session 存入 redis，不使用缓存，默认为 MemorySessionDAO
        DefaultWebSessionManager sessionManager = applicationContext.getBean(DefaultWebSessionManager.class, "sessionManager");
        sessionManager.setSessionDAO(sessionDAO);
    }
}
